#include "watcher.h"
#include <iostream>
#include <strings.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <cstdlib>
#include <cstring>
#include <sys/inotify.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <fcntl.h>
#include "g_headers.h"
#include <pthread.h>
using namespace std;

Watcher::Watcher(bool &p_update):updated(p_update)
{


//	if( (tsf_sock = socket(AF_INET,SOCK_STREAM,0)) < 0 )
//	{
//		cerr << "open_tcp error" << endl;
//		exit(-1);
//	}

	bzero(&srv_ctl,sizeof(srv_ctl));
	bzero(&srv_tsf,sizeof(srv_tsf));

	init_sock(CTL,&srv_ctl);
	init_sock(TSF,&srv_tsf);

//	usr = _usr;
//	pwd = _pwd;

	if( (sem_id = semget(SEM_KEY, 2, 0666|IPC_CREAT)) < 0 )
	{
		perror("semget");
		exit(-1);
	}

	if( init_semvalue(sem_id, 2) < 0 )
	{
		cerr << "init_semvalue error" << endl;
		exit(-1);
	}
}

void Watcher::set_usr_info(string& usr, string& pwd)
{
	this -> usr = usr;
	this -> pwd = pwd;
}

int Watcher::get_sock(int flag)
{
	if( flag == CTL )
	{
		return ctl_sock;
	} 
	else
	{
	}
}

void Watcher::init_sock(int kind, struct sockaddr_in* sock_addr)
{
	sock_addr->sin_family = AF_INET;

	if( kind == CTL )
	{
		sock_addr->sin_port = htons(SRV_CTL_PT);
	} 
	else
	{
		sock_addr->sin_port = htons(SRV_TSF_PT); 
	}

	if( inet_pton(AF_INET,SRV_IP, &(sock_addr->sin_addr)) < 0 )
	{
		perror("inet_pton");
		exit(1);
	}
}

void Watcher::watch(vector<string>& all_path)
{
	fd_nofity = inotify_init();
	if( fd_nofity < 0 )
	{
		perror("inotify_inint");
		exit(-1);
	}

	uint32_t wd;

	//对所有gsyncer文件夹中的子文件夹进行监控 
	for( int i = 0 ; i < all_path.size() ; i++ )
	{
		if( (wd = inotify_add_watch(fd_nofity,all_path[i].c_str(), 
						IN_ALL_EVENTS)) < 0 )
		{
			perror("add_watch");
			exit(-1);
		}
		
		watch_list[wd] = all_path[i].c_str(); 
	}
	
	int res;
	struct inotify_event* event;
	char buf[1024];
	struct event* e_ptr;

	int i=0;
	while( true )
	{
	    
		res = read(fd_nofity,buf,1024);
		if( res < 0 )
		{
			perror("read");
			continue;
		}

		while(i < res)
		{
			event = (struct inotify_event *) &buf[i];
			//如果是目录自己的变动，就抛弃
			if( event->len == 0 )
			{
				break;
			}

			//过滤临时文件
			if(file_filter(event->name) == true )
			{
				break;
			}

			if( event->mask & IN_CREATE )
			{
			    
				//如果是目录 则加入MAP并通知服务器
				//if( is_dir(event->wd,event->name) )
                                if(event->mask & IN_ISDIR)
				{
					uint32_t wd_temp;
					string name_temp = watch_list[event->wd] + "/";
					name_temp += event->name;

					if( (wd_temp = inotify_add_watch(fd_nofity,name_temp.c_str(), 
								IN_ALL_EVENTS)) < 0 )
					{
						perror("add_watch");
						exit(-1);
					}
		
					watch_list[wd_temp] = name_temp.c_str(); 
					
					build_event(CREATE,name_temp,e_ptr);
					cout<<"creat dir "<<name_temp<<endl;
					sem_wait(sem_id,MUTEX);
					event_queue.push(e_ptr);
					sem_signal(sem_id,MUTEX);
					sem_signal(sem_id,QUEUE_N);
					
					//	print_event(event->wd,event->name,CREATE);
				}
				else
				{
				    //file is created 
				    string name_temp = watch_list[event->wd] + "/";
				    name_temp += event->name;
				    build_event(CREATE_F,name_temp,e_ptr);
				    cout<<"create file "<<name_temp<<endl;
				    sem_wait(sem_id,MUTEX);
				    event_queue.push(e_ptr);
				    sem_signal(sem_id,MUTEX);
				    sem_signal(sem_id,QUEUE_N);
				}

			}
			if( event->mask & IN_DELETE || event->mask & IN_MOVED_FROM)
			{
				uint32_t wd_temp;
				string name_temp = watch_list[event->wd] + "/";
				name_temp += event->name;

				/*bool is_dir_ = false;
				map<int,string>::iterator map_itr;
				for( map_itr = watch_list.begin(); map_itr != watch_list.end() ;map_itr++ )
				{
					if(map_itr->second == name_temp)
					{
						wd_temp = map_itr->first;
						is_dir_ = true;
							break;
					}
				}
				*/
				if( event->mask & IN_ISDIR )
				{
					//if( inotify_rm_watch(fd_nofity,wd_temp) < 0 )
					//{
					//	perror("inotify_rm_watch");
					//	exit(-1);
					//}
				    //这里需要处理删除监控
					build_event(DEL_D,name_temp,e_ptr);
					cout<<"del dir "<<name_temp<<endl;
				}
				else
				{
					build_event(DEL_F,name_temp,e_ptr);
					cout<<"del file "<<name_temp<<endl;
				}
				
				sem_wait(sem_id,MUTEX);
				event_queue.push(e_ptr);
				sem_signal(sem_id,MUTEX);
				sem_signal(sem_id,QUEUE_N);

				//print_event(event->wd,event->name,DELETE);
			}
			if( event->mask & IN_CLOSE_WRITE || event->mask & IN_MODIFY )
			{
				string name_temp = watch_list[event->wd] + "/";
				name_temp += event->name;
				build_event(UP_F,name_temp,e_ptr);
				
				cout<<"modify or close write file "<<name_temp<<endl;
				
				sem_wait(sem_id,MUTEX);
				event_queue.push(e_ptr);
				sem_signal(sem_id,MUTEX);
				sem_signal(sem_id,QUEUE_N);

				//print_event(event->wd,event->name,UPDATE);
			}
			if( event->mask & IN_MOVED_TO )
			{
				string name_temp = watch_list[event->wd] + "/";
				name_temp += event->name;

				
				if(event->mask & IN_ISDIR )
				{
					vector<string> moved_path;
					moved_path.push_back(name_temp);
					dir_search(moved_path,name_temp);
					add_watch(moved_path);
					string path=watch_list[event->wd] ;
					chdir(path.c_str());
					string f_name = event->name;	
					string cmd = "zip -rq \"/tmp/";
					cmd += f_name;
					cmd += ".zip\" \"";
					cmd += f_name;
					cmd+="\"";
					
					system(cmd.c_str());
					chdir("-");
					build_event(UP_D,name_temp,e_ptr);
					cout<<"zip-cmd "<<cmd<<endl;
					cout<<"in moved to dir "<<name_temp<<endl;
				}
				else
				{
				    cout<<"in moved to file "<<name_temp<<endl;
				    build_event(UP_F,name_temp,e_ptr);
				}
				
				sem_wait(sem_id,MUTEX);
				event_queue.push(e_ptr);
				sem_signal(sem_id,MUTEX);
				sem_signal(sem_id,QUEUE_N);
				//print_event(event->wd,event->name,UPDATE);
			}

			i = i + sizeof(struct inotify_event) + event->len;
		}

		i = 0;
	}
}


void Watcher::add_watch(vector<string>& all_path)
{
	uint32_t wd;

	for( int i = 0 ; i < all_path.size() ; i++ )
	{
		wd = inotify_add_watch(fd_nofity,all_path[i].c_str(),IN_ALL_EVENTS);
		if( wd < 0 )
		{
			perror("inotify_add");
			exit(-1);
		}

		watch_list[wd] = all_path[i];
	}
	
}

//int Watcher::file_delete(const char* name)
//{
//	struct udp_pkt_2* pkt = new struct udp_pkt_2;

	//打包
//	strncpy(pkt->file,name,FILE_LEN);
//	strncpy(pkt->user,"ace",USR_LEN);
//	pkt->type = 3;

	
//	delete pkt;
//}

bool Watcher::file_filter(const char* file)
{
	//文件过滤器  过滤各种编辑器产生的临时文件
	if( strcmp(file,"4913") == 0 )
	{
		return true;
	}

	if( file[0] == '.' )
	{
		return true;
	}

	if( file[strlen(file)-1] == '~' )
	{
		return true;
	}

	return false;
}

void Watcher::print_event(struct event* event)
{

	cout << "----" << endl;
	cout << event->path << " ";

	switch ( event->option )
	{
		case CREATE :
			cout << "creat";
			break;
		case DEL_F :
			cout << "delete";
			break;
		case UP_F :
			cout << "update";
		default :
			break;
	}

	cout << endl << "----" << endl;
}

bool Watcher::is_dir(int wd, const char* name)
{
	string name_temp = watch_list[wd] + "/";
	name_temp += name;

	struct stat buf;
	
//	cout << name_temp << endl;
	if( stat(name_temp.c_str(),&buf) < 0 )
	{
		perror("stat");
		exit(-1);
	}

	return S_ISDIR(buf.st_mode);
}

void Watcher::build_event(int option, string& path, struct event*& event)
{
	event = new struct event;
	event->option = option;
	event->path = path;
}

void Watcher::cre_or_del(int option, struct event* event)
{
        this->updated = true;
        ctl_pkg_t info;
        char temp_path[PATH_LEN];
        strncpy(temp_path,(event->path).c_str(),PATH_LEN);

        int i, counter = 0;
        for( i = 0 ; i < strlen(temp_path) ; i++)
        {
            if(temp_path[i] == '/')
                counter++;

            if(counter == 4)
                break;
        }

        snprintf(info.str.path, PATH_LEN, "%s%s", SRV_ROOT, temp_path + i);
        info.type = event->option;

	Writen(ctl_sock,&info,sizeof(info));

        int8_t reply;
        if(readn(ctl_sock,&reply,sizeof(int8_t)) < 0)
            perror("read");
}

void Watcher::cexit()
{
	struct event * p = new struct event;
	p -> option = EXIT;

	sem_wait(sem_id,MUTEX);
	event_queue.push(p);
	sem_signal(sem_id,MUTEX);
	sem_signal(sem_id,QUEUE_N);
}

void Watcher::process_events()
{
	struct event* event;

	while( true )
	{
	    //cout << "1" << endl;
	    sem_wait(sem_id,QUEUE_N);
	    sem_wait(sem_id,MUTEX);
	    event = event_queue.front();
	    event_queue.pop();
	    sem_signal(sem_id,MUTEX);

	    
	    switch ( event->option )
		{
		case CREATE :
		    cre_or_del(CREATE,event);
		    break;
		case CREATE_F:
		    cre_or_del(CREATE_F,event);
		    break;
		case DEL_F :
		    cre_or_del(DEL_F,event);
		    break;
		case DEL_D :
		    cre_or_del(DEL_D,event);
		    break;
		case UP_F :
                    update(event);
		    break;
		case UP_D :
                    //update(event);
		    break;
		case EXIT :
			//cre_or_del(EXIT,event);
			//shutdown(ctl_sock,SHUT_WR);
                        pthread_exit(NULL);
			break;
		default :
		    break;
		}
	    delete event;
	}
}

void Watcher::update(struct event* event)
{

    this->updated = true;
    ctl_pkg_t event_info;

    event_info.type = event->option;

    char temp_path[PATH_LEN];
    strncpy(temp_path,(event->path).c_str(),PATH_LEN);

    int i, counter = 0;
    for( i = 0 ; i < strlen(temp_path) ; i++)
    {
        if(temp_path[i] == '/')
            counter++;

        if(counter == 4)
            break;
    }

    //char path[PATH_LEN];
    snprintf(event_info.str.path, PATH_LEN, "%s%s", SRV_ROOT, temp_path + i);
    //strncpy(event_info.str.path,(event->path).c_str(),PATH_LEN);
    Writen(ctl_sock,&event_info,sizeof(event_info));
    cout << event_info.str.path << endl;

    int8_t reply;
    read(ctl_sock,&reply,sizeof(int8_t));
    if( reply != 1 )
	{
	    cerr << "reply error" << endl;
            cout<<(int)reply<<endl;
	    exit(-1);
	}
    
    int tsf_sock = socket(AF_INET,SOCK_STREAM,0);
    if( connect(tsf_sock, (SA*)&srv_tsf, sizeof(srv_tsf)) < 0 )
	{
	    perror("connect");
	    exit(-1);
	}
    
    //发送PUSH请求
    uint8_t request = DATA_PUSH;
    if( write(tsf_sock,&request,sizeof(uint8_t)) < 0 )
    {
        perror("send request to 1235");
        return;
    }

    //发送用户名
    char usr_name[USR_LEN];
    strncpy(usr_name,this->usr.c_str(),USR_LEN);
    if( writen(tsf_sock,usr_name,USR_LEN) < 0)
    {
        perror("send usr_name to 1235");
        return;
    }

    //发送目录
    //snprintf(event_info.str.path, PATH_LEN, "%s%s", SRV_ROOT, temp_path + i);

    cout << event_info.str.path << endl;
    if(writen(tsf_sock,event_info.str.path,PATH_LEN) < 0 )
    {
        perror("send path to 1235");
        return;
    }

    int fd_temp = open((event->path).c_str(),O_RDONLY);
    if( fd_temp < 0 )
    {
        cout<<"file "<<event->path<<" not exists now \n";
        return;

    }
    //Writen(tsf_sock,&head, sizeof(head));

    file_copy(tsf_sock,fd_temp);
    close(fd_temp);
    close(tsf_sock);

    
//    if( event->option == UP_F )
//	{
//	    int fd_temp = open((event->path).c_str(),O_RDONLY);
//	    if( fd_temp < 0 )
//		{
//		    cout<<"file "<<event->path<<" not exists now \n";
//		    return;

//		}
//	    Writen(tsf_sock,&head, sizeof(head));
	    
//	    file_copy(tsf_sock,fd_temp);
//	    close(fd_temp);
//	    close(tsf_sock);
//	}
//    else
//	{
//		event->path+=".zip";
//		strncpy(head.path, (event->path).c_str(),PATH_LEN);
	
//		int i;
//		for( i=(event->path).length()-1;i>=0;--i)
//		{
//			if((event->path)[i]=='/')
//				break;
//		}
	
//		string file_name = (event->path).substr(i+1,(event->path).length()-i-1);
//		string path = "/tmp/";
//		path+=file_name;
//		int fd_temp = open((path).c_str(),O_RDONLY);
	
//		if( fd_temp < 0 )
//	    {
//			cout<<"zip file "<<event->path<<" not exists now \n";
//			return;
//            }
		
//		Writen(tsf_sock,&head, sizeof(head));
	
//		file_copy(tsf_sock,fd_temp);
	
//		close(fd_temp);
//		close(tsf_sock);
//		unlink(path.c_str());
//    }
}
